
#include <iostream>
#include <string>
#include <cstring>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>

const static int default_sockfd = -1;
const static size_t default_retry_interval = 2;
const static size_t default_max_retries = 5;

// 枚举连接状态 
// 一般的 enum 是整数，不会做类型检查
// C++11 强类型枚举，会做语法检查
enum class Status
{
    NEW,           // 新建状态，就是单纯的连接
    CONNECTING,    // 正在连接，仅仅方便查询 conn 状态
    CONNECTED,     // 连接或者重连成功
    DISCONNECTED,  // 失联，重连失败
    CLOSED         // 连接失败，经历重连，无法连接
};

// 枚举退出码标志
enum ExitCode
{
    USAGE_ERROR = 1, 
    SOCKET_ERROR
};


class ClientConnection   // 一个连接 
{
private:
public:
    ClientConnection(const std::string& server_ip, in_port_t server_port)
        : _sockfd(default_sockfd), 
          _server_ip(server_ip), 
          _server_port(server_port), 
          _status(Status::NEW), 
          _retry_interval(default_retry_interval), 
          _max_retries(default_max_retries)
    {}

    void Connect()       // 发起连接 
    {
        _sockfd = socket(AF_INET, SOCK_STREAM, 0);
        if (_sockfd < 0)
        {
            std::cerr << "Create socket error, errno: " << errno << " -> " << strerror(errno) << std::endl;
            exit(ExitCode::SOCKET_ERROR);
        }

        struct sockaddr_in server;
        memset(&server, 0, sizeof server);
        server.sin_family = AF_INET;
        server.sin_port = ::htons(_server_port);
        ::inet_pton(AF_INET, _server_ip.c_str(), &server.sin_addr);
        socklen_t len = sizeof server;

        int n = connect(_sockfd, (struct sockaddr*)&server, len);
        if (n < 0)
        {
            // 连接失败，直接断开连接，每一次都重新开始 
            std::cerr << "Connect error, errno: " << errno << " -> " << strerror(errno) << std::endl;
            // 先关闭 _sockfd ，文件描述符也是系统资源 
            Disconnect();
            // 更新状态 
            _status = Status::DISCONNECTED;
            // 返回
            return;
        }
        _status = Status::CONNECTED;
        std::cout << "connect success" << std::endl;
    }

    void Process()       // 正常的 IO 通信
    {
        while (true)
        {
            sleep(1);
            std::string msg = "Hello Server";
            ssize_t n = send(_sockfd, msg.c_str(), msg.size(), 0);
            if (n > 0)
            {
                char inBuffer[1024];
                ssize_t m = recv(_sockfd, inBuffer, (sizeof inBuffer) - 1, 0);
                if (m > 0)
                {
                    inBuffer[m] = 0;
                    std::cout << "Server echo# " << inBuffer << std::endl;
                }
                else if (m == 0)
                {
                    std::cerr << "Recv Nothing?" << std::endl;
                    _status = Status::DISCONNECTED;
                    break;
                }
                else
                {
                    // 有可能服务器掉线
                    // 有可能读取有问题
                    std::cerr << "Recv error, errno: " << errno << " -> " << strerror(errno) << std::endl;
                    _status = Status::DISCONNECTED;
                    break;
                }
            }
            else if (n == 0)
            {
                std::cerr << "Send Nothing?" << std::endl;
                _status = Status::DISCONNECTED;
                break;
            }
            else
            {
                std::cerr << "Send error, errno: " << errno << " -> " << strerror(errno) << std::endl;
                _status = Status::CLOSED;
                break;
            }
        }
    }

    void Reconnect()     // 重连
    {
        int cnt = 0;
        while (true)
        {
            ++cnt;
            if (cnt > _max_retries)
            {
                _status = Status::CLOSED;
                std::cout << "Reconnection failed. Please check the network." << std::endl;
                break;
            }
            _status = Status::CONNECTING;
            std::cout << "Disconnected line reconnecting.... (" << cnt << " times)" << std::endl;
            sleep(_retry_interval);
            Connect();
            if (_status == Status::CONNECTED)
            {
                break;
            }
        }
    }

    void Disconnect()    // 关闭连接
    {
        if (_sockfd > default_sockfd)
        {
            ::close(_sockfd);
            _sockfd = default_sockfd;
            _status = Status::CLOSED;
        }
    }

    Status GetStatus()
    {
        return _status;
    }

    ~ClientConnection()
    {}

private:
    int _sockfd;
    std::string _server_ip;
    in_port_t _server_port;
    Status _status;
    size_t _retry_interval;  // 两次重连的时间间隔
    size_t _max_retries;     // 重连次数 
};



class TcpClient
{
private:
public:
    TcpClient(const std::string& server_ip, in_port_t server_port)
        : _connection(server_ip, server_port)
    {}

    void Execute()
    {
        while (true)
        {
            switch (_connection.GetStatus())
            {
            case Status::NEW:
                _connection.Connect();
                break;
            case Status::CONNECTED:
                _connection.Process();
                break;
            case Status::DISCONNECTED:
                _connection.Reconnect();
                break;
            case Status::CLOSED:
                _connection.Disconnect();
                return;
            default:
                // Do Nothing
                break;
            }
        }
    }

    ~TcpClient()
    {}

private:
    ClientConnection _connection;  // 建立好的一个 TCP 连接 
};



void Usage(const std::string& process)
{
    std::cout << "Usage: " << process << " Server-IP Server-Port" << std::endl;
}


// TcpClient Server-IP Server-Port
int main(int argc, char* argv[])
{
    if (argc != 3)
    {
        Usage(argv[0]);
        exit(ExitCode::USAGE_ERROR);
    }

    std::string ServerIP = argv[1];
    in_port_t ServerPort = std::stoi(argv[2]);

    // 如果客户端比较大，使用 new 或者智能指针比较好
    TcpClient tcpClient(ServerIP, ServerPort);
    tcpClient.Execute();

    return 0;
}

